//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <stdio.h>
#include <stdlib.h>
#include "CWStringInputStream.h"
#include "_CWStringInputStream.cpp"

#define ROUND2(n)       (((n) + 1) & ~1)   // round up to multiple of 2 bytes

ECode CWStringInputStream::Available(
    /* [out] */ Int32 * pBytes)
{
    if (!pBytes) {
        return E_INVALID_ARGUMENT;
    }

    if (!m_pBuf) {
        return E_CLOSED_STREAM;
    }

    *pBytes = (m_tail - m_pos) / sizeof(WChar);
    return NOERROR;
}

ECode CWStringInputStream::Close()
{
    m_pBuf = NULL;
    return NOERROR;
}

ECode CWStringInputStream::Mark(
    /* [in] */ Int32 readLimit)
{
    if (!m_pBuf) {
        return E_CLOSED_STREAM;
    }

    return NOERROR;
}

ECode CWStringInputStream::IsMarkSupported(
    /* [out] */ Boolean * pSupported)
{
    if (!pSupported) {
        return E_INVALID_ARGUMENT;
    }

    if (!m_pBuf) {
        return E_CLOSED_STREAM;
    }

    *pSupported = FALSE;

    return NOERROR;
}

ECode CWStringInputStream::Read(
    /* [out] */ Byte * pByte)
{
    if (!pByte) {
        return E_INVALID_ARGUMENT;
    }

    if (!m_pBuf) {
        return E_CLOSED_STREAM;
    }
    if (m_pos >= m_tail) {
        return E_OUT_OF_STREAM;
    }

    *pByte = *((Byte *)m_pBuf + m_pos);
    m_pos++;
    return NOERROR;
}

ECode CWStringInputStream::ReadBuffer(
    /* [out] */ BufferOf<Byte> * pBuffer)
{
    if (pBuffer == NULL || !pBuffer->GetCapacity()) {
        return E_INVALID_ARGUMENT;
    }

    if (!m_pBuf) {
        return E_CLOSED_STREAM;
    }

    return ReadBufferEx(0, pBuffer->GetCapacity(), pBuffer);
}

ECode CWStringInputStream::ReadBufferEx(
    /* [in] */ Int32 offset,
    /* [in] */ Int32 length,
    /* [out] */ BufferOf<Byte> * pBuffer)
{
    if (pBuffer == NULL || offset < 0 || length < 0
        || (pBuffer->GetCapacity() < length + offset)
        || (pBuffer->GetUsed() < offset)) {
        return E_INVALID_ARGUMENT;
    }
    if (!m_pBuf) {
        return E_CLOSED_STREAM;
    }

    if (m_pos >= m_tail) {
        return E_OUT_OF_STREAM;
    }
    if (m_pos + length > m_tail) {
        length = m_tail - m_pos;
    }

    pBuffer->Replace(offset, (Byte *)m_pBuf + m_pos, length);
    m_pos += length;

    return NOERROR;
}

ECode CWStringInputStream::Reset()
{
    if (!m_pBuf) {
        return E_CLOSED_STREAM;
    }
    m_pos = 0;
    return NOERROR;
}

ECode CWStringInputStream::Skip(
    /* [in] */ Int32 length)
{
    if (length <= 0) {
        return E_INVALID_ARGUMENT;
    }

    if (!m_pBuf) {
        return E_CLOSED_STREAM;
    }

    m_pos = ROUND2(m_pos);

    length = length * sizeof(WChar);
    Int32 len = m_tail - m_pos;
    if (length > len)  {
        length = len;
    }
    m_pos += length;

    return NOERROR;
}

ECode CWStringInputStream::ReadWChar(
    /* [out] */ WChar * pCharacter)
{
    if (!pCharacter) {
        return E_INVALID_ARGUMENT;
    }

    if (!m_pBuf) {
        return E_CLOSED_STREAM;
    }

    if (m_pos > (Int32)(m_tail - sizeof(WChar))) {
        return E_OUT_OF_STREAM;
    }

    m_pos = ROUND2(m_pos);

    *pCharacter = m_pBuf[m_pos / sizeof(WChar)];
    m_pos += sizeof(WChar);
    return NOERROR;
}

ECode CWStringInputStream::ReadWCharArray(
    /* [out] */ BufferOf<WChar> * pcharArray)
{
    if (pcharArray == NULL || !pcharArray->GetCapacity()) {
        return E_INVALID_ARGUMENT;
    }

    if (!m_pBuf) {
        return E_CLOSED_STREAM;
    }

    return ReadWCharArrayEx(0, pcharArray->GetCapacity(), pcharArray);
}

ECode CWStringInputStream::ReadWCharArrayEx(
    /* [in] */ Int32 offset,
    /* [in] */ Int32 length,
    /* [out] */ BufferOf<WChar> * pcharArray)
{
    if (pcharArray == NULL || offset < 0 || length < 0
        || pcharArray->GetCapacity() < length + offset
        || pcharArray->GetUsed() < offset) {
        return E_INVALID_ARGUMENT;
    }

    if (!m_pBuf) {
        return E_CLOSED_STREAM;
    }

    if (m_pos > (Int32)(m_tail - sizeof(WChar))) {
        return E_OUT_OF_STREAM;
    }
    m_pos = ROUND2(m_pos);

    length *= sizeof(WChar);
    if (m_pos + length > m_tail) {
        length = m_tail - m_pos;
    }

    pcharArray->Replace(offset, m_pBuf + m_pos/sizeof(WChar), length / sizeof(WChar));
    m_pos += length;

    return NOERROR;
}

ECode CWStringInputStream::ReadWString(
    /* [out] */ WStringBuf * pString)
{
    if (pString == NULL || !pString->GetCapacity()) {
        return E_INVALID_ARGUMENT;
    }

    if (!m_pBuf) {
        return E_CLOSED_STREAM;
    }

    if (m_pos > (Int32)(m_tail - sizeof(WChar))) {
        return E_OUT_OF_STREAM;
    }
    m_pos = ROUND2(m_pos);

    pString->Copy((wchar_t *)m_pBuf + m_pos / sizeof(WChar));

    Int32 len = m_tail - m_pos;
    if (pString->GetCapacity() < (Int32)(len / sizeof(WChar))) {
        len = pString->GetCapacity() * sizeof(WChar);
    }
    m_pos += len;

    return NOERROR;
}

ECode CWStringInputStream::constructor(
    /* [in] */ WString string)
{
    if (string.IsNull()) {
        return E_INVALID_ARGUMENT;
    }

    m_pBuf = (wchar_t *)(const wchar_t *)string;
    m_tail = string.GetLength() * sizeof(WChar);
    m_pos = 0;
    return NOERROR;
}
